package com.vic.concurrency.completablefuture;

import java.util.concurrent.*;

/**
 *
 * Future例子
 * Future无法解决多个异步任务需要相互依赖的场景
 * @author victor
 *
 * Ref
 * https://mp.weixin.qq.com/s/ipQLDlCc6a2BeYeEkwOwMg
 * https://mp.weixin.qq.com/s/VCRtwaJ99p5FzBl0xlClIg
 * https://mp.weixin.qq.com/s/JM6Clc8qu3uJR0j1Tz8Ojg
 */
public class Test1 {

    public static void main(String[] args) throws Exception {


//        testCompletableGet();

        // CompletableFuture
//        testCompletableFuture1();

        // CountDownLatch
//        testCountDownLatch();
        
        
        // Future
//        testFuture();
    }

    public static void testCompletableGet() throws Exception {

        CompletableFuture<String> cp1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "商品A";
        });

        // getNow方法测试，如果还在计算中，则返回"商品B"
        System.out.println(cp1.getNow("商品B"));

        //join方法测试
//        CompletableFuture<Integer> cp2 = CompletableFuture.supplyAsync((() -> 1 / 0));
        /*
        join方法获取结果方法里不会抛异常，但是执行结果会抛异常，抛出的异常为CompletionException。
        Exception in thread "main" java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
            at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273)
            at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280)
            at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1606)
            at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1596)
            at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
            at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1067)
            at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1703)
            at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:172)
        Caused by: java.lang.ArithmeticException: / by zero
            at com.vic.concurrency.completablefuture.Test1.lambda$testCompletableGet$1(Test1.java:46)
            at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1604)
            ... 5 more
         */
//        System.out.println(cp2.join());

        System.out.println("-----------------------------------------------------");

        //get方法测试
        CompletableFuture<Integer> cp3 = CompletableFuture.supplyAsync((() -> 1 / 0));
        /*
        get方法获取结果方法里将抛出异常，执行结果抛出的异常为ExecutionException。
        Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zero
            at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
            at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908)
            at com.vic.concurrency.completablefuture.Test1.testCompletableGet(Test1.java:53)
            at com.vic.concurrency.completablefuture.Test1.main(Test1.java:18)
        Caused by: java.lang.ArithmeticException: / by zero
            at com.vic.concurrency.completablefuture.Test1.lambda$testCompletableGet$1(Test1.java:52)
            at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1604)
            at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1596)
            at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
            at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1067)
            at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1703)
            at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:172)
         */
        System.out.println(cp3.get());
    }


    private static void testCompletableFuture1() throws Exception {
        long startTime = System.currentTimeMillis();

        //调用用户服务获取用户基本信息
        CompletableFuture<String> userFuture = CompletableFuture.supplyAsync(() ->
                //模拟查询商品耗时500毫秒
        {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "用户A";
        });

        //调用商品服务获取商品基本信息
        CompletableFuture<String> goodsFuture = CompletableFuture.supplyAsync(() ->
                //模拟查询商品耗时500毫秒
        {
            try {
                Thread.sleep(400);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "商品A";
        });

        System.out.println("获取用户信息:" + userFuture.get());
        System.out.println("获取商品信息:" + goodsFuture.get());

        //模拟主程序耗时时间
        Thread.sleep(600);
        System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
    }

    private static void testCountDownLatch() throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        CountDownLatch downLatch = new CountDownLatch(2);
        long startTime = System.currentTimeMillis();
        Future<String> userFuture = executorService.submit(() -> {
            //模拟查询商品耗时500毫秒
            Thread.sleep(500);
            downLatch.countDown();
            return "用户A";
        });

        Future<String> goodsFuture = executorService.submit(() -> {
            //模拟查询商品耗时500毫秒
            Thread.sleep(400);
            downLatch.countDown();
            return "商品A";
        });

        downLatch.await();
        //模拟主程序耗时时间
        Thread.sleep(600);
        System.out.println("获取用户信息:" + userFuture.get());
        System.out.println("获取商品信息:" + goodsFuture.get());
        System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
    }

    private static void testFuture() throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        Future<String> future = executorService.submit(() -> {
            Thread.sleep(2000);
            return "hello";
        });
        System.out.println(future.get());
        System.out.println("end");
    }
}
